package com.baiyangtx.lingerweather.service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;

import com.baiyangtx.lingerweather.adapt.CityKey;
import com.baiyangtx.lingerweather.data.CityDataOpenHelper;
import com.baiyangtx.lingerweather.net.ForecastTask;
import com.baiyangtx.lingerweather.net.RealTimeTask;
import com.baiyangtx.lingerweather.net.SyncTask;
import com.baiyangtx.lingerweather.observer.WeatherObserver;



/**
 * 
 * @author BaiyangTX
 *
 */
public class DataService extends Service  {

	/**
	 * 
	 */
	public static final String SUBCITY_FILENAME = "sub_city" ;
	
	
	/*
	 * 
	 */
	private final IBinder mBinder = new MBinder() ;
	
	/*
	 * 
	 */
	private Map<String, String> subjectCity = null ;
	
	/*
	 * 
	 */
	private Set<WeatherObserver> observers = new HashSet<WeatherObserver>() ;
	
	@SuppressLint("SimpleDateFormat")
	private SimpleDateFormat format = new SimpleDateFormat(CityKey.FORMAT) ;
	
	private UpdateSubjecCityAsyncTask updateTask = null ;
	
	
	/*
	 * 
	 */
	private CityDataOpenHelper database ;
	
	
	/**
	 * 
	 * @author BaiyangTX
	 *
	 */
	public class MBinder extends Binder {
		
		 public DataService getService(){
			 return DataService.this ;
		 }
		
	}
	
	
	/**
	 * 
	 * @author BaiyangTX
	 *
	 */
	private class UpdateSubjecCityAsyncTask extends AsyncTask<SyncTask, Integer, Map<String , String> >{
		private static final int IOEXCEPTION = 1 ;
		private static final int THROWABLE = 2 ;
		@Override
		protected Map<String , String> doInBackground(SyncTask... params) {
			
			for( SyncTask task : params ){
				try {
					Map<String , String> res = task.syncCity() ;
					subjectCity.putAll(res) ;
				}  catch (IOException e) {
					e.printStackTrace();
					publishProgress(IOEXCEPTION);
					return null ;
				} catch( Throwable t ){
					t.printStackTrace() ;
					publishProgress(THROWABLE);
					return null ;
				}
			}
			Date date = new Date() ;
			String time = format.format(date) ;
			subjectCity.put(CityKey.SYNC_TIME, time ) ;
			saveCityInformation(subjectCity) ;
			return new HashMap<String, String>(subjectCity) ;
		}
		
		
		
		@Override
		protected void onProgressUpdate(Integer... values) {
			if(values[0].equals(IOEXCEPTION)){
				Toast.makeText(DataService.this, "网络异常，请稍检查网络", Toast.LENGTH_LONG).show() ;
			}else{
				Toast.makeText(DataService.this, "系统异常，请稍后再试", Toast.LENGTH_LONG).show() ;
			}
			super.onProgressUpdate(values);
		}
		
		
		@Override
		protected void onPostExecute(Map<String , String> result) {
			if( result == null ){
				return ;
			}
			for( WeatherObserver o : observers ){
				o.onWeatherUpdata(result) ;
			}
			super.onPostExecute(result);
		}
		
	}
	
	
	
	

	@Override
	public IBinder onBind(Intent intent) {
		initService();
		return mBinder ;
	}
	
	
	
	
	
	
	
	/**
	 * 
	 * @param wo
	 */
	public void attachWeatherObserver(WeatherObserver wo){
		observers.add(wo) ;
	}
	
	/**
	 * 
	 * @param wo
	 */
	public void detachWeatherObserver(WeatherObserver wo ){
		observers.remove(wo) ;
	}
	
	
	
	
	/**
	 * 
	 * @param cityCode
	 * @param cityName
	 */
	public void setSubjectCity(String cityCode , String cityName ){
		if( cityCode == null ){
			subjectCity = null ;
		}else{
			Map<String , String > city = getCity(cityCode) ;
			if( city != null ){
				subjectCity = city ;
			}else{
				this.subjectCity = new HashMap<String, String>();
				this.subjectCity.put( CityKey.CITY_NAME , cityName ) ;
				this.subjectCity.put( CityKey.CITY_CODE , cityCode ) ;
			}
		}
		saveSubjectCityCode(cityCode) ;
		updateTask = null ;
		syncSubjectCity() ;
		for( WeatherObserver o : observers ){
			o.onSubjectCityChange(cityName);
		}
	}
	
	
	
	/**
	 * 
	 * @return
	 */
	public Map<String , String> getSubjectCity(){
		if( subjectCity != null ){
			return new HashMap<String, String>(subjectCity) ;
		}
		return null ;
	}
	
	
	
	
	/**
	 * 
	 * @param code
	 * @param name
	 */
	public void addCity(String code , String name ){
		SQLiteDatabase db = database.getWritableDatabase() ;
		ContentValues cValues = new ContentValues() ;
		cValues.put(CityKey.CITY_CODE, code) ;
		cValues.put(CityKey.CITY_NAME, name ) ;
		db.insert(CityDataOpenHelper.TABLE_NAME	, null , cValues ) ;
		db.close() ;
	}
	
	
	/**
	 * 
	 * @param code
	 * @return
	 */
	public boolean deleteCity(String code){
		SQLiteDatabase db = database.getWritableDatabase() ;
		String[] args = {code} ;
		int count = db.delete(CityDataOpenHelper.TABLE_NAME, CityKey.CITY_CODE+"=?", args ) ;
		db.close() ;
		if( code == subjectCity.get(CityKey.CITY_CODE ) ){
			Set<Map<String , String>> set = listAllUserCities() ;
			if( set.size() > 0 ){
				Map<String , String> city = set.iterator().next() ;
				setSubjectCity( city.get(CityKey.CITY_CODE), city.get(CityKey.CITY_NAME) ) ;
			}else{
				setSubjectCity(null, null) ;
			}
		}
		return count == 0 ? false : true  ;
	}
	
	
	/**
	 * 
	 * @return
	 */
	public Set<Map<String, String>> listAllUserCities(){
		SQLiteDatabase db = database.getWritableDatabase() ;
		String table = CityDataOpenHelper.TABLE_NAME ;
		String[] columns = { CityKey.CITY_CODE , CityKey.CITY_NAME , CityKey.REAL_TEMPERATURE , CityKey.RECENT_WEATHER } ;
		Cursor cursor = db.query(table, columns, null, null	, null , null , null ) ;
		Set<Map<String, String>> set = new HashSet<Map<String,String>>() ;
		while( cursor.moveToNext() ){
			Map<String , String > city = new HashMap<String, String>() ;
			for( String key : columns ){
				int index = cursor.getColumnIndex(key) ;
				String value = cursor.getString(index) ;
				city.put(key, value) ;
			}
			set.add(city) ;
		}
		return set ;
	}
	
	
	
	/**
	 * 
	 * @param code
	 * @return
	 */
	public Map<String , String> getCity(String code){
		SQLiteDatabase db = database.getReadableDatabase() ;
		
		String[] selectionArgs = {code} ;
		Cursor cursor = db.query(CityDataOpenHelper.TABLE_NAME , null , 
				CityKey.CITY_CODE+"=?", selectionArgs, null , null , null ) ;
		
		
		if( cursor.getCount() < 1 ){
			return null ;
		}
		cursor.moveToNext() ;
		
		Map<String , String > city = new HashMap<String, String>() ;
		String[] keys = cursor.getColumnNames() ;
		for( String key : keys ){
			int index = cursor.getColumnIndex(key) ;
			String value = cursor.getString(index) ;
			city.put(key, value) ;
		}
		cursor.close() ;
		db.close() ;
		return city ;
	}
	
	
	
	/**
	 * 
	 */
	public void syncSubjectCity(){
		if( updateTask != null ){
			AsyncTask.Status status =  updateTask.getStatus() ;
			if( status.equals(AsyncTask.Status.RUNNING)){
				return ;
			}
		}
		updateTask = new UpdateSubjecCityAsyncTask() ;
		String code = subjectCity.get(CityKey.CITY_CODE) ;
		SyncTask ftask = new ForecastTask(code) ;
		SyncTask rtask = new RealTimeTask(code) ;
		updateTask.execute( ftask , rtask ) ;
	}
	
	
	

	
	
	
	
	
	/*
	 * initializing the service. check database, initializing subject city information, etc..
	 */
	private void initService(){
		database = new CityDataOpenHelper(getApplicationContext()) ;
		try {
			FileInputStream ins = openFileInput(SUBCITY_FILENAME );
			BufferedReader reader = new BufferedReader(new InputStreamReader(ins) ) ;
			String code = reader.readLine() ;
			System.out.println("Subject : "+code);
			if( code != null ){
				subjectCity = getCity(code) ;
			}
		} catch (IOException e) {
			e.printStackTrace();
			return ;
		} 
	}

	
	/*
	 * save the information of a city to database.
	 */
	private void saveCityInformation(Map<String , String > city ){
		SQLiteDatabase db = database.getWritableDatabase() ;
		ContentValues cValues = new ContentValues() ;
		Set<String> key = city.keySet() ;
		for(String k : key){
			cValues.put(k, city.get(k) ) ;
		}
		String[] args = { city.get(CityKey.CITY_CODE)} ;
		db.update( CityDataOpenHelper.TABLE_NAME , cValues, CityKey.CITY_CODE + "=?", args) ;
		db.close() ;
	}
	
	
	/*
	 * save the subject city code to file.
	 */
	private void saveSubjectCityCode(String code){
		try {
			FileOutputStream fo = openFileOutput(SUBCITY_FILENAME, MODE_PRIVATE) ;
			BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(fo) ) ;
			if( code == null ){
				code = "" ;
			}
			writer.write(code) ;
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
}
